#pragma once

#include <exception>
#include <string>
#include <regex>
#include <set>
#include <vector>
#include "../../jsonxx/json.hpp"
#include "../../jsonxx/fifo_map.hpp"

template <class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
using FifoJson = jsonxx::basic_json<my_workaround_fifo_map>;

using std::set;
using std::string;

class ParseException : std::exception {
public:
    int linePos;
    int charPos;
    string msg;
    ParseException() = default;
    ParseException(string msg): msg(msg){}
    ParseException(int linePos, int charPos, string msg)
        : linePos(linePos), charPos(charPos), msg(msg) {}
};

template <typename Enumeration>
inline auto enumLiteral(Enumeration const value) ->
    typename std::underlying_type<Enumeration>::type {
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

template<typename T> 
bool contains(const set<T> &cont, const T &val){
    return cont.count(val);
}

template <typename Container, typename T>
bool contains(const Container &cont, const T &val) {
    for (auto i : cont) {
        if (i == val)
            return true;
    }
    return false;
}

/**
 * @return max key in the map
 */ 
template <typename Map> 
int maxKey(const Map &mp) {
    int ret = -1;
    for (auto [i, _] : mp) {
        ret = i > ret ? i : ret;
    }
    return ret;
}

template <typename Container_T, typename T>
int indexOf(Container_T &container, T &value) {
    for (int i = 0; i < container.size(); ++i) {
        if (container[i] == value)
            return i;
    }
    return -1;
}

string mytrim_copy(const string &s) {
    if (s.empty())
        return s;
    int stPos = s.find_first_not_of(" ");
    if (stPos == string::npos)
        return "";
    return s.substr(stPos, s.find_last_not_of(" ") + 1);
}

string myreplace_copy(const string &str, const string &reg, const string &target) {
    return std::regex_replace(str, std::regex(reg), target);
}

/**
 * if tokenCompress is set to true, then continuous delimiter would treated as one
 */ 
std::vector<std::string> mysplit(const std::string &str, const std::string &regStr,
                               bool tokenCompress = true, bool dotrim = true) {
    std::regex reg(regStr);
    std::vector<std::string> v(std::sregex_token_iterator(str.begin(), str.end(), reg, -1),
                               std::sregex_token_iterator());
    if (!tokenCompress)
        return v;
    std::vector<std::string> newV;
    for (auto i : v) {
        if (!i.empty()) {
            if(dotrim)
                newV.push_back(mytrim_copy(i));
            else
                newV.push_back(i);
        }
    }
    return newV;
}

bool myStartsWith(const std::string &str, const std::string &start){
    if (str.size() < start.size())
        return false;
    for (int i = 0; i < start.size(); ++i) {
        if (str[i] != start[i]){
            return false;
        }
    }
    return true;
}

bool myEndsWith(const std::string &str, const std::string &end){
    if (str.size() < end.size())
        return false;
    int n = str.size();
    for (int i = 0; i < end.size(); ++i) {
        if (str[n - i - 1] != end[i]) {
            return false;
        }
    }
    return true;
}